package cn.huaqingcheng.tianshu.infrastructure.sys.adapter;

import cn.huaqingcheng.tianshu.common.core.FileTool;
import cn.huaqingcheng.tianshu.common.core.model.AttributeMap;
import cn.huaqingcheng.tianshu.common.core.model.TemporaryUrl;
import cn.huaqingcheng.tianshu.core.sys.adapter.FileInfo;
import cn.huaqingcheng.tianshu.core.sys.adapter.FileStorageService;
import cn.huaqingcheng.tianshu.core.sys.model.SysFileScheme;
import com.aliyun.sdk.service.oss2.OSSClient;
import com.aliyun.sdk.service.oss2.OSSClientBuilder;
import com.aliyun.sdk.service.oss2.PresignOptions;
import com.aliyun.sdk.service.oss2.credentials.StaticCredentialsProvider;
import com.aliyun.sdk.service.oss2.models.GetObjectRequest;
import com.aliyun.sdk.service.oss2.models.GetObjectResult;
import com.aliyun.sdk.service.oss2.models.HeadObjectRequest;
import com.aliyun.sdk.service.oss2.models.HeadObjectResult;
import com.aliyun.sdk.service.oss2.models.PresignResult;
import com.aliyun.sdk.service.oss2.models.PutObjectRequest;
import com.aliyun.sdk.service.oss2.transport.BinaryData;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.util.FastByteArrayOutputStream;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.time.Duration;
import java.util.Map;

/**
 * 阿里云对象存储服务
 */
@Component
@RequiredArgsConstructor
public class AlibabaCloudObjectStorageService implements FileStorageService {

    private final AlibabaCloudObjectStorageProperties properties;

    private OSSClient ossClient;

    @SuppressWarnings("MethodWithMoreThanThreeNegations")
    @PostConstruct
    public void init() {
        String accessKeyId = properties.getAccessKeyId();
        String secret = properties.getAccessKeySecret();
        String region = properties.getRegion();

        OSSClientBuilder builder = OSSClient.newBuilder()
                .credentialsProvider(new StaticCredentialsProvider(accessKeyId, secret))
                .region(region);

        String endpoint = properties.getEndpoint();
        if (endpoint != null) {
            builder.endpoint(endpoint);
        }

        Boolean useCName = properties.getUseCName();
        if (useCName != null) {
            builder.useCName(useCName);
        }

        Boolean useInternalEndpoint = properties.getUseInternalEndpoint();
        if (useInternalEndpoint != null) {
            builder.useInternalEndpoint(useInternalEndpoint);
        }

        Boolean useAccelerateEndpoint = properties.getUseAccelerateEndpoint();
        if (useAccelerateEndpoint != null) {
            builder.useAccelerateEndpoint(useAccelerateEndpoint);
        }

        Boolean disableSsl = properties.getDisableSsl();
        if (disableSsl != null) {
            builder.disableSsl(disableSsl);
        }

        Duration timeout = properties.getConnectTimeout();
        if (timeout != null) {
            builder.connectTimeout(timeout);
        }

        Duration writeTimeout = properties.getReadWriteTimeout();
        if (writeTimeout != null) {
            builder.readWriteTimeout(writeTimeout);
        }

        ossClient = builder.build();
    }

    @PreDestroy
    public void destroy() {
        try {
            ossClient.close();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public boolean support(SysFileScheme scheme) {
        return scheme == SysFileScheme.ALI_OSS;
    }

    @Override
    public FileInfo save(InputStream is, String path, AttributeMap<Object> attrs) {
        String bucket = properties.getBucket();
        String key = FileTool.removePrefixSeparator(path);

        try (
                is;
                FastByteArrayOutputStream os = new FastByteArrayOutputStream()
        ) {
            is.transferTo(os);

            PutObjectRequest request = PutObjectRequest.newBuilder()
                    .bucket(bucket)
                    .key(key)
                    .cacheControl("public, max-age=31536000, immutable, no-transform")
                    .body(BinaryData.fromStream(os.getInputStream()))
                    .build();
            ossClient.putObject(request);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        HeadObjectRequest headObjectRequest = HeadObjectRequest.newBuilder()
                .bucket(bucket)
                .key(key)
                .build();
        HeadObjectResult headObjectResult = ossClient.headObject(headObjectRequest);

        String type = headObjectResult.contentType();
        Long length = headObjectResult.contentLength();

        return new FileInfo(type, length, SysFileScheme.ALI_OSS.customSchemeUrl(bucket, path));
    }

    @Override
    public InputStream read(URI uri) {
        GetObjectRequest request = GetObjectRequest.newBuilder()
                .bucket(uri.getHost())
                .key(FileTool.removePrefixSeparator(uri.getPath()))
                .build();
        GetObjectResult object = ossClient.getObject(request);
        return object.body();
    }

    @Override
    public TemporaryUrl temporaryUrl(URI uri) {
        GetObjectRequest request = GetObjectRequest.newBuilder()
                .bucket(uri.getHost())
                .key(FileTool.removePrefixSeparator(uri.getPath()))
                .build();
        PresignOptions options = PresignOptions.newBuilder()
                .expiration(properties.getPresignExpiration())
                .build();
        PresignResult result = ossClient.presign(request, options);

        return new TemporaryUrl(
                result.method(),
                result.url(),
                result.signedHeaders().orElse(Map.of()),
                result.expiration().orElse(null)
        );
    }

}
